﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace CoreCommon
{
    /// <summary>
    /// Сохраняемый динамический список. 
    /// Предназначен для хранения списка элементов в файловой системе. Программы могут читать и изменять список через 
    /// механизм сериализации.
    /// Каждому элементу в списке соответствует некоторое число, что-то вроде "индекса цитируемости", его значение
    /// зависит от того, сколько раз элемент сохранялся в списке. При добавлении элемента в список, 
    /// проверяется его наличие в этом списке, и если элемент найден, то изменяется связанный с ним 
    /// "индекс цитируемости", если элемент не найден, то создается новый элемент, и с ним
    /// связывается "индекс цитируемости" минимального значения.                     
    /// При создании списка, можно указать его глубину - количество сохраняемых в списке элементов. 
    /// До тех пор пока количество размещенных в списке элементов меньше его глубины, новые элементы продолжают добавляються. 
    /// Когда же достигнута максимальная глубина, то новый элемент - добавляется, а один из старых - удаляется. 
    /// Удаляться будет тот, чей "индекс цитируемости" минимален.
    /// </summary>
    [Serializable]
    public class SaveList
    {
        /// <summary>
        /// Конструктор по умолачанию
        /// </summary>
        public SaveList() { }
        
        /// <summary>
        /// Конструктор задающий колличество поддерживаемых списком значений
        /// </summary>
        /// <param name="startItemCount">Кличество элементов поддерживаемых списком. По умолчанию 10</param>
        public SaveList(int startItemCount)
            : this()
        {
            this.startItemCount = startItemCount;
        }
        
        /// <summary>
        /// Конструтор задающий количество элементов поддерживаемых списком и максимальный индекс, при достижении
        /// которого, будет произведена реорганизация индекса.
        /// </summary>
        /// <param name="startItemCount">Число элементов списка</param>
        /// <param name="reorganizeSize">Максимальный размер индекса, по достижении которого будет произведена реогранизация индекса</param>
        public SaveList(int startItemCount, int reorganizeSize)
            : this(startItemCount)
        {
            this.reorganizeSize = reorganizeSize;
        }

        /// <summary>
        /// Добавление элемента в список
        /// </summary>
        /// <param name="item">Элемент списка</param>
        public void Add(SaveItem item)
        {
            findItem = item;
            SaveItem currItem = sl.Find(FindItem);
            if (currItem == null)
            {
                if (sl.Count == startItemCount)
                {
                    SaveItem min = sl.Min();
                    sl.Remove(min);
                }
                item.Count = 1;
                sl.Add(item);
            }
            else
            {
                currItem.Count++;

                // Реорганизация списка
                if (currItem.Count == reorganizeSize)
                    ReorganizeList();
            }
        }

        /// <summary>
        /// Возвращает коллекцию элементов
        /// </summary>
        public ReadOnlyCollection<SaveItem> ListItems
        {
            get
            {
                return new ReadOnlyCollection<SaveItem>(sl);
            }
        }
        
        /// <summary>
        /// Количество элементов поддерживаемых списком
        /// </summary>
        public int StartItemCount
        {
            get { return startItemCount; }
            set { startItemCount = value; }
        }
        
        /// <summary>
        /// Максимальный размер индекса, по достижении которого будет произведена реогранизация индекса
        /// </summary>
        public int ReorganizeSize
        {
            get { return reorganizeSize; }
            set { reorganizeSize = value; }
        }

        #region Private

        private List<SaveItem> sl = new List<SaveItem>();
        private SaveItem findItem;
        private int startItemCount = 10;
        private int reorganizeSize = 10000;
        private bool FindItem(SaveItem si)
        {
            return si.Id == findItem.Id && si.Name == findItem.Name;
        }
        private void ReorganizeList()
        {
            var orderedList = from f in sl
                              orderby (f.Count)
                              select f;
            int i = 0;
            foreach (SaveItem saveItem in orderedList)
                saveItem.Count = i++;
        }

        #endregion
    }

    /// <summary>
    /// Класс представляющий элемент для сохранения в списке
    /// </summary>
    [Serializable]
    public class SaveItem : IComparable
    {
        /// <summary>
        /// Конструктор по умолчанию
        /// </summary>
        public SaveItem() { }
        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="id">Идентификатор элемента</param>
        /// <param name="name">Имя элемента</param>
        public SaveItem(int id, string name)
            : this()
        {
            this.Id = id;
            this.Name = name;
        }
        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="id">Идентификатор элемента</param>
        /// <param name="name">Имя элемента</param>
        /// <param name="altName">Краткое наименование элемента</param>
        public SaveItem(int id, string name, string altName)
            : this(id, name)
        {
            this.AltName = altName;
        }

        /// <summary>
        /// Сравнение двух элементов списка
        /// </summary>
        /// <param name="obj">Элемент для сравнения</param>
        /// <returns>True - если элементы одинаковы, false - в обратном случае</returns>
        public int CompareTo(object obj)
        {
            SaveItem saveItem = obj as SaveItem;
            if (saveItem == null)
                throw new InvalidCastException();
            else
            {
                if (saveItem.Count > this.Count)
                    return -1;
                else if (saveItem.Count == this.Count)
                    return 0;
                else
                    return 1;
            }
        }
        /// <summary>
        /// Метод сравнения
        /// </summary>
        /// <param name="obj">Элемент для сравнения</param>
        /// <returns>True - если элементы одинаковы, false - в обратном случае</returns>
        public override bool Equals(object obj)
        {
            if (!(obj is SaveItem))
                return false;
            return (this.CompareTo(obj) == 0);
        }
        /// <summary>
        /// Хэш элемента
        /// </summary>
        /// <returns>Значение хэша</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        /// <summary>
        /// Перекрытый оператор сравнения элеметов
        /// </summary>
        /// <param name="saveItem1">Первый элемент</param>
        /// <param name="saveItem2">Второй элемент</param>
        /// <returns>True - если элементы одинаковы, false - в обратном случае</returns>
        public static bool operator ==(SaveItem saveItem1, SaveItem saveItem2)
        {
            if (object.ReferenceEquals(saveItem1, saveItem2))
                return true;
            if (object.ReferenceEquals(saveItem1, null))
                return false;
            return saveItem1.Equals(saveItem2);
        }
        /// <summary>
        /// Перекрытый оператор неравенства
        /// </summary>
        /// <param name="saveItem1">Первый элемент</param>
        /// <param name="saveItem2">Второй элемент</param>
        /// <returns>True - если элементы неодинаковы, false - в обратном случае</returns>
        public static bool operator !=(SaveItem saveItem1, SaveItem saveItem2)
        {
            return !(saveItem1 == saveItem2);
        }
        /// <summary>
        /// Перекрытый оператор "меньше"
        /// </summary>
        /// <param name="saveItem1">Первый элемент</param>
        /// <param name="saveItem2">Второй элемент</param>
        /// <returns>True - если первый элемент меньше, false - в обратном случае</returns>
        public static bool operator <(SaveItem saveItem1, SaveItem saveItem2)
        {
            return (saveItem1.CompareTo(saveItem2) < 0);
        }
        /// <summary>
        /// Перекрытый оператор "больше"
        /// </summary>
        /// <param name="saveItem1">Первый элемент</param>
        /// <param name="saveItem2">Второй элемент</param>
        /// <returns>True - если первый элемент больше, false - в обратном случае</returns>
        public static bool operator >(SaveItem saveItem1, SaveItem saveItem2)
        {
            return (saveItem1.CompareTo(saveItem2) > 0);
        }

        /// <summary>
        /// Идентификатор элемента
        /// </summary>
        public int Id { get; set; }
        /// <summary>
        /// Имя элемента
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Альтернативное имя элемнта
        /// </summary>
        public string AltName { get; set; }
        /// <summary>
        /// Количество элементов
        /// </summary>
        public int Count { get; set; }
    }

}
